﻿using System.ComponentModel;
using System.Reflection;
using JBeamLibrary.Events;
using JBeamLibrary.Exceptions;
using JBeamLibrary.Tools;
using JBeamLibrary.VehicleParts.Collections;
using JBeamLibrary.VehicleParts.Elements;
using JBeamLibrary.VehicleParts.Enums;
using JBeamLibrary.VehicleParts.Interfaces;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace JBeamLibrary.VehicleParts.Groups
{
    public abstract class VehicleSection<T> where T : class, IVehicleElement
    {
        #region Fields

        private readonly VehicleSectionProperties<T> _properties;

        public event ElementPropertyChangedEventHandler ElementPropertyChanged;

        #endregion

        #region Indexers

        public T this[int index]
        {
            get
            {
                if (index >= Count || index < 0)
                    return null;
                return ElementCollection[index];
            }
        }

        #endregion

        #region Properties

        public VehiclePart Part { get; private set; }

        public abstract string Name { get; }

        public abstract IVehicleGroupCollection<T> ElementCollection { get; }

        public VehicleSectionProperties<T> Properties
        {
            get { return _properties; }
        }

        public bool IsEmpty
        {
            get { return ElementCollection.Count == 0 && Properties.Count == 0; }
        }

        public int Count
        {
            get { return ElementCollection.Count; }
        }

        public int LastPropertyIndex
        {
            get { return HasProperties ? Properties.Last().ElementIndex : -1; }
        }

        public int HighIndex
        {
            get { return Math.Max(Count - 1, LastPropertyIndex); }
        }

        public bool HasProperties
        {
            get { return Properties.Count > 0; }
        }

        #endregion

        #region CTORs

        protected VehicleSection(VehiclePart part)
        {
            Part = part;
            _properties = new VehicleSectionProperties<T>(this);
            Initialize();
        }

        #endregion

        #region Methods

        #region Elements

        private void Initialize()
        {
            ElementCollection.ElementPropertyChanged += (s, e) =>
            {
                if (ElementPropertyChanged != null)
                    ElementPropertyChanged(s, e);
            };
        }

        public void AddElement(T element)
        {
            try
            {
                ElementCollection.Add(element);
            }
            catch (ArgumentException ex)
            {
                throw new InvalidElementException<T>(this, element, ex);
            }
        }

        public void RemoveElement(T element)
        {
            ElementCollection.Remove(element);
        }

        public void RemoveElementAt(int index)
        {
            ElementCollection.RemoveAt(index);
        }

        public int IndexOfElement(T element)
        {
            return ElementCollection.IndexOf(element);
        }

        #endregion

        #region Properties

        public void SetPropertyValue(int elementIndex, JProperty prop, ElementPropertyUse use)
        {
            _properties.SetPropertyValue(elementIndex, prop, use);
        }

        public void SetPropertyValue(int elementIndex, ElementProperties property, ElementPropertyUse use, object value)
        {
            _properties.SetPropertyValue(elementIndex, property, use, value);
        }

        public void SetPropertyValue(int elementIndex, string propertyName, ElementPropertyUse use, object value)
        {
            _properties.SetPropertyValue(elementIndex, propertyName, use, value);
        }

        public object GetPropertyValue(int elementIndex, ElementProperties property, ElementPropertyUse use)
        {
            return _properties.GetPropertyValue(elementIndex, property, use);
        }

        public object GetPropertyValue(int elementIndex, string propertyName, ElementPropertyUse use)
        {
            return _properties.GetPropertyValue(elementIndex, propertyName, use);
        }

        public object GetLastPropertyValue(ElementProperties property, ElementPropertyUse use)
        {
            return _properties.GetLastPropertyValue(property, use);
        }

        public object GetLastPropertyValue(string propertyName, ElementPropertyUse use)
        {
            return _properties.GetLastPropertyValue(propertyName, use);
        }

        public bool HasDefinedProperty(ElementProperties property, ElementPropertyUse use)
        {
            return _properties.SelectMany(list => list).Any(prop => prop.Property == property && prop.Use == use);
        }

        public bool HasDefinedProperty(string propertyName, ElementPropertyUse use)
        {
            try
            {
                var prop = GroupHelper.GetPropertyFromName(propertyName);
                return HasDefinedProperty(prop, use);
            }
            catch (UnknownPropertyException ex)
            {
                return false;
            }
        }

        #endregion

        #endregion
    }
}
